import { Page, ElementHandle } from 'puppeteer';
import { Scraper } from '../scraper';
import { Logger } from '@nestjs/common';
import { scrapeSleep, shuffleArray } from '../utils/util';
import { StringElementHandleTuple } from '../types/index';
import { IGoods, IScraperProp } from '../types';
import { error } from 'console';

// 淘宝特有的选择器配置
const SELECTORS = {
  nextPage:
    'button.next-btn.next-small.next-btn-normal.next-pagination-item.next-next',
  shop: 'a.doubleCardWrapperAdapt--mEcC7olq',
  goodsList: 'div.valueItem--GzWd2LsV',
  goodsPrice: 'div.displayPrice--oD54EJg0 span.text--fZ9NUhyQ',
  shopName: 'span.shopName--mTDZGIPO',
  goodsName: 'valueItem--GzWd2LsV isSelected--YrA6x4Yj',
} as const;

// 最大爬的页数
const MAX_PAGE = 3;

export default class TaoBaoScraper extends Scraper {
  cookie: string | undefined;
  taskId: number;
  constructor({ url, keyword, ...props }: IScraperProp) {
    super({ url, keyword, ...props });
    this.taskId = Number(props.taskId);
  }

  // 提取 SKU ID
  private extractSkuId(url: string): string | null {
    try {
      const parsedUrl = new URL(url);
      return parsedUrl.searchParams.get('skuId');
    } catch (err) {
      Logger.error(`解析 URL 失败: ${url}, 错误: ${err}`);
      return null;
    }
  }

  // 检查是否存在下一页（带重试）
  private async hasNextPage(
    page: Page,
    selector: string,
    retries = 3,
  ): Promise<boolean> {
    for (let attempt = 0; attempt < retries; attempt++) {
      try {
        const nextButton = await page.$(selector);
        if (!nextButton) return false;

        const isDisabled = await page.$eval(selector, (el) =>
          el.hasAttribute('disabled'),
        );
        return !isDisabled;
      } catch (err) {
        Logger.error(`检查下一页失败，第 ${attempt + 1} 次重试: ${err}`);
        await page.reload();
        await scrapeSleep(20000, 30000);

        if (attempt === retries - 1) {
          Logger.error(`重试 ${retries} 次后失败`);
          return false;
        }
      }
    }
    return false;
  }

  // 点击进入下一页（带重试）
  private async goToNextPage(
    page: Page,
    selector: string,
    retries = 3,
  ): Promise<boolean> {
    try {
      await Promise.all([
        page.click(selector, { button: 'left', delay: 100 }),
        page.waitForNavigation({ waitUntil: 'networkidle2' }),
      ]);
      Logger.debug('成功进入下一页');
      return true;
    } catch (err) {
      Logger.error(`点击下一页失败: ${err}`);
      if (retries <= 1) {
        Logger.error('重试次数已达上限');
        return false;
      }

      Logger.debug(`等待 10-20 秒后重试...`);
      await page.reload();
      await scrapeSleep(10000, 20000);
      return this.goToNextPage(page, selector, retries - 1);
    }
  }

  // 打开搜索页面并输入关键词
  private async search(page: Page): Promise<Page> {
    try {
      await page.goto(this.url, { waitUntil: 'load' });
      await page.type('input.search-combobox-input', this.keyword);
      await scrapeSleep();
      await page.click('button.btn-search');
      await page.waitForNavigation({ waitUntil: 'networkidle2' });
      return page;
    } catch (err) {
      Logger.error(`打开搜索页面失败: ${err}`);
      throw err;
    }
  }

  // 主爬取逻辑
  public async scraping() {
    if (isNaN(this.taskId)) {
      Logger.error(`Scraping Task ID is invalid!`);
      return;
    }

    const page = await this.launchBrowser(false);
    if (!page) {
      Logger.error('no active page found');
      return;
    }
    Logger.log(`goto ${this.url}`);

    try {
      const searchPage = await this.search(page);
      await this.monitorHumanScroll(searchPage);
      await scrapeSleep(3000, 3000);

      await this.scrapeSearchResultPage(searchPage);
    } catch (err) {
      Logger.error(`爬取失败: ${err}`);
    } finally {
      if (this.browser) {
        await this.browser.close();
      }
    }
  }

  // 爬取搜索结果页
  private async scrapeSearchResultPage(page: Page): Promise<void> {
    let pageCount = 1;

    while (true) {
      await scrapeSleep(5000, 60000);

      const shops = await this.getAllShops(page, SELECTORS.shop);
      const shuffledShops = shuffleArray<StringElementHandleTuple>(shops);

      if (shuffledShops.length === 0) {
        Logger.warn('未找到任何店铺元素');
        break;
      }

      const originalUrl = page.url();
      for (const [skuId, shopElement] of shuffledShops) {
        if (!skuId) continue; // 跳过无 SKU 的店铺

        await scrapeSleep(5000, 20000);
        const goodsList: IGoods[] = await this.visitShop(
          page,
          shopElement,
          originalUrl,
          skuId,
        );
        Logger.debug(`=================>${skuId}`);
        this.store(goodsList); //存入数据库
      }

      pageCount++;
      if (pageCount > MAX_PAGE) break;

      const hasNext = await this.hasNextPage(page, SELECTORS.nextPage, 3);
      if (!hasNext) {
        Logger.log('已到达最后一页');
        break;
      }

      const success = await this.goToNextPage(page, SELECTORS.nextPage, 5);
      if (!success) {
        Logger.error('进入下一页失败');
        break;
      } else {
        Logger.log('进入下一页成功。');
      }
    }
    Logger.log('网页爬取完成');
  }

  // 获取所有店铺
  private async getAllShops(
    page: Page,
    selector: string,
  ): Promise<StringElementHandleTuple[]> {
    const shops: StringElementHandleTuple[] = [];
    const elements = await page.$$(selector);

    for (const element of elements) {
      const href = await page.evaluate(
        (el) => el.getAttribute('href'),
        element,
      );
      if (href && href.trim()) {
        const fullHref = href.startsWith('http') ? href : `https:${href}`;
        const skuId = this.extractSkuId(fullHref);
        if (skuId) shops.push([skuId, element]); // 只添加有 SKU 的店铺
      }
    }
    return shops;
  }

  // 访问单个店铺
  private async visitShop(
    page: Page,
    shopElement: ElementHandle,
    originalUrl: string,
    skuId: string,
  ): Promise<IGoods[]> {
    await this.scrollToElement(page, shopElement);
    await scrapeSleep(500, 2000);
    let goodsList: IGoods[];
    const newPage = await this.openNewTab(shopElement);
    if (!newPage) {
      Logger.warn('无法打开新标签页');
      return [];
    }
    const shopUrl = newPage.url();
    try {
      await newPage.bringToFront();
      Logger.debug(`访问店铺，新标签页 URL: ${shopUrl}`);

      await newPage.waitForNetworkIdle({ timeout: 10000 });
      const goodsElements = await this.getFilteredGoodsElements(newPage);
      goodsList = await this.clickAndScrapeGoodsDetails(
        newPage,
        goodsElements,
        skuId,
        shopUrl,
      );
    } catch (err) {
      Logger.error(`处理店铺 ${shopUrl} 失败: ${err}`);
      return [];
    } finally {
      await newPage.close();
      Logger.debug('新标签页已关闭');
    }

    await scrapeSleep(1000, 3000);
    await page.bringToFront();
    if (page.url() !== originalUrl) {
      await page.goto(originalUrl, { waitUntil: 'networkidle2' });
    }
    Logger.debug(`返回原始页面: ${page.url()}`);
    return goodsList;
  }

  // 打开新标签页
  private async openNewTab(element: ElementHandle): Promise<Page | null> {
    if (!this.browser) {
      throw error('打开新页面失败,浏览器对象丢失。');
    }
    const [newPage] = await Promise.all([
      new Promise<Page | null>((resolve) =>
        this.browser!.once('targetcreated', (target) => resolve(target.page())),
      ),
      element.click({ button: 'middle' }),
    ]);
    return newPage;
  }

  // 获取过滤后的商品元素
  private async getFilteredGoodsElements(page: Page): Promise<ElementHandle[]> {
    try {
      await page.waitForSelector(SELECTORS.goodsList, { timeout: 10000 });
      const elements = await page.$$(SELECTORS.goodsList);

      const filteredElements: ElementHandle[] = [];
      for (const element of elements) {
        const isDisabled = await element.evaluate((el: Element) =>
          el.getAttribute('class')?.includes('isDisabled'),
        );
        if (isDisabled) continue;

        const text = await element.evaluate((el) => el.textContent || '');
        if (text.includes(this.keyword)) {
          Logger.debug(`匹配商品: ${text}`);
          filteredElements.push(element);
        }
      }
      return filteredElements;
    } catch (err) {
      Logger.error(`获取商品元素失败: ${err}`);
      return [];
    }
  }

  // 点击并抓取商品详情
  private async clickAndScrapeGoodsDetails(
    page: Page,
    goodsElements: ElementHandle[],
    skuId: string,
    shopUrl: string,
  ): Promise<IGoods[]> {
    Logger.debug(`===店铺有${goodsElements.length}个商品满足条件===`);
    const result: IGoods[] = [];
    for (const element of goodsElements) {
      try {
        await this.mouseRandomly(page);
        await this.scrollToElement(page, element);
        await scrapeSleep(2000, 6000);

        const goods = await this.processGoods(page, element, skuId, shopUrl);
        result.push(goods);
      } catch (err) {
        Logger.debug(`跳过商品: ${err}`);
      }
    }
    return result;
  }

  // 处理单个商品
  private async processGoods(
    page: Page,
    element: ElementHandle,
    skuId: string,
    url: string,
  ): Promise<IGoods> {
    await element.click({ delay: 1500 });
    await scrapeSleep(1000, 5000);

    const { goods_name, goods_price, shop_name } = await this.fetchGoodsDetails(
      page,
      element,
    );
    const g: IGoods = {
      taskId: this.taskId,
      source: 'taobao',
      skuId: skuId,
      skuName: goods_name,
      shopPrice: goods_price,
      platformPrice: goods_price,
      shopName: shop_name,
      detail: url,
      updateTime: new Date(),
    };

    Logger.log(`商品数据: ${JSON.stringify(g)}`);
    return g;
  }

  // 获取商品详情
  private async fetchGoodsDetails(
    page: Page,
    element: ElementHandle,
  ): Promise<{
    goods_name: string;
    goods_price: number;
    shop_name: string;
  }> {
    const goodsName = await element.evaluate(
      (el) => el?.textContent || '',
      await element.$(SELECTORS.goodsName),
    );
    const goodsPrice = await page.$eval(
      SELECTORS.goodsPrice,
      (el) => el.textContent || '',
    );
    const shopName = await page.$eval(
      SELECTORS.shopName,
      (el) => el.textContent || '',
    );

    return {
      goods_name: goodsName,
      goods_price: Number(goodsPrice),
      shop_name: shopName,
    };
  }
}
